#pragma once

#include "style.h"

namespace sfc::fmt {

template <class Fmter>
class DebugTuple {
  Fmter& _fmt;
  usize _cnt = 0;

 public:
  explicit DebugTuple(Fmter& fmt) : _fmt{fmt} {
    _fmt.write_str("(");
  }

  ~DebugTuple() {
    _fmt.write_str(")");
  }

  DebugTuple(const DebugTuple&) = delete;

  void entry(const auto& val) {
    if (_cnt != 0) {
      _fmt.write_str(", ");
    }
    _fmt.write(val);
    _cnt += 1;
  }

  void entries(auto&& iter) {
    iter.for_each([&](auto&& val) { this->entrie(val); });
  }
};

template <class Fmter>
class DebugList {
  Fmter& _fmt;
  usize _cnt = 0;

 public:
  explicit DebugList(Fmter& fmt) : _fmt{fmt} {
    _fmt.write_str("[");
  }

  ~DebugList() {
    _fmt.write_str("]");
  }

  DebugList(const DebugList&) noexcept = delete;

  void entry(const auto& val) {
    if (_cnt != 0) {
      _fmt.write_str(", ");
    }
    _fmt.write(val);
    _cnt += 1;
  }

  void entries(auto&& iter) {
    iter->for_each([&](auto&& val) { this->entry(val); });
  }
};

template <class Fmter>
class DebugSet {
  Fmter& _fmt;
  usize _cnt = 0;

 public:
  explicit DebugSet(Fmter& fmt) : _fmt{fmt} {
    _fmt.write_str("{");
  }

  ~DebugSet() {
    if (!_fmt) {
      return;
    }
    _fmt.write_str("}");
  }

  DebugSet(const DebugSet&) noexcept = delete;

  void entry(const auto& val) {
    if (_cnt != 0) {
      _fmt.write_str(", ");
    }
    _fmt.write(val);
    _cnt += 1;
  }

  void entries(auto&& iter) {
    iter.for_each([&](auto&& val) { this->entrie(val); });
  }
};

template <class Fmter>
class DebugMap {
  Fmter& _fmt;
  usize _cnt = 0;

 public:
  explicit DebugMap(Fmter& fmt) : _fmt{fmt} {
    _fmt.write_str("{");
  }

  ~DebugMap() {
    _fmt.write_str("}");
  }

  DebugMap(const DebugMap&) noexcept = delete;

  void entry(const auto& name, const auto& value) {
    if (_cnt != 0) {
      _fmt.write_str(", ");
    }

    _fmt.write_str("\"");
    _fmt.write_str(name);
    _fmt.write_str("\": ");
    _fmt.write(value);

    _cnt += 1;
  }

  void entries(auto iter) {
    iter->for_each([&](auto&& item) {  //
      this->entry(item._0, item._1);
    });
  }
};

template <class Fmter>
class DebugStruct {
  Fmter& _fmt;
  usize _cnt = 0;

 public:
  explicit DebugStruct(Fmter& fmt) : _fmt{fmt} {
    _fmt.write_str("{");
  }

  ~DebugStruct() {
    _fmt.write_str("}");
  }

  DebugStruct(DebugStruct&&) noexcept = delete;

  void field(const auto& name, const auto& value) {
    if (_cnt != 0) {
      _fmt.write_str(", ");
    }

    _fmt.write_str(name);
    _fmt.write_str(": ");
    _fmt.write(value);

    _cnt += 1;
  }

  void fields(auto iter) {
    iter.for_each([&](auto&& item) {  //
      const auto& [k, v] = item;
      this->entry(k, v);
    });
  }
};

}  // namespace sfc::fmt
